﻿%%%%%%%%%%%%%%%%% 导言 %%%%%%%%%%%%%%%%%%%%%%%%

%================================
% begin: 文档字体设置
%================================
% 加载 Simplefonts 模块
\usemodule[simplefonts]
\usemodule[t-pretty-c]

\setupinteraction[state=start] % don't forget this line!
% make chapter, section bookmarks visible when opening document
\placebookmarks[chapter,section,subsection][chapter,section]
\setupinteractionscreen[option=bookmark]

% 设置西文字体
\setmainfont[yaheiconsolashybrid]
\setsansfont[yaheiconsolashybrid]
\setmonofont[yaheiconsolashybrid]

% 设置中文字体
\setcjkmainfont[AdobeSongStd-Light]
\setcjksansfont[AdobeSongStd-Light]
\setcjkmonofont[AdobeSongStd-Light]

 
% 设置中文断行
\setscript[hanzi]

% 设置文档默认字体尺寸
\setupsimplefonts[size=11pt]

\definebodyfont [10pt,11pt,12pt] [rm]
[tfe=yaheiconsolashybrid at 48pt]
%end

%================================
% begin: 版面设计
%================================
% 开启彩色支持，并定义一种颜色
\setupcolors[state=start]
\definecolor[headingcolor][r=.6,g=.4,b=.6]

% 版面布局
\setuppapersize[A4][A4]
\setuplayout[topspace=0.5in, backspace=1in, header=24pt, footer=36pt]

% 标题样式
\setupheads[indentnext=yes]
\setuphead[title]	[style={\ss\bfd},	color=headingcolor,header=empty,before={},
  	after={\rightline{\tf 大孟 $\langle$\from[author-email]$\rangle$}\bigskip\bigskip}]
\setuphead[section,subject][color=headingcolor,style={\ss\bfa}]

% 段落首行缩进
\setupindenting[always,2em,first]

% 列表样式
\setupitemgroup[itemize][packed]

% 页脚与页码
\setupfooter[style=\it]
\setupfootertexts[\date\hfill Object Oriented C 入门指南]
\setuppagenumbering[location={header,right}, style=bold]

% 插图样式
\setuplabeltext[en][figure=\tfx 图\;]
\setupfloats[spacebefore=small,spaceafter=small,sidespacebefore=small,sidespaceafter=small]
\setupcaptions[style=\tfx]
% end

%================================
% begin: 一些杂项
%================================
% PDF 文档属性信息
\setupinteraction[state=start, title={Hello，OOC World!}, subtitle={使用 C语言 进行面向对象的开发}, author={大孟}, keyword={Object Oriented C}]

% 定义几个“超级链接”，可在正文中使用
\useURL[author-email][pingf0@gmail.com][][pingf0@gmail.com]
\useURL[ooc][http://code.google.com/p/ooc-gcc/][][OOC-GCC]
\useURL[dameng][mailto:pingf0@gmail.com][][pingf0@gmail.com]
\useURL[liyanrui][http://garfileo.is-programmer.com/][][garfield]
\useURL[qkevin][http://www.qkevin.com/][][qkevin]
\useURL[ekd123][http://ekd123.is-programmer.com/][][ekd123]
\useURL[fleurer][me.ssword@gmail.com][][fleurer]
\useURL[wuhao][][][wuhao]
% end
 
 
  
%%%%%%%%%%%%%%%%% 正文 %%%%%%%%%%%%%%%%%%%%%%%%

\starttext
\title{Hello，OOC World!}
\blank[3cm]
\midaligned {这份Tutorial性质的文档其实很扯的,非要啰嗦些用C语言进行面向对象开发的基本内容}

\page
\subject{版权声明}
\blank[10cm]
\starttyping
Copyright (C)  2011-2011  Jesse Meng.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
A copy of the license is included in the section entitled "GNU
Free Documentation License".
\stoptyping

\blank[5cm]
\from[ooc]为开源软件,遵循LGPL3协议发布,本手册为相关配套文档,采用GFDL协议发布,本手册中提供的相关示例代码以及简化版的OOC-LITE
需遵循GPL3协议.

任何使用请遵循相关协议,如有和协议相冲突的特殊要求,请联系我 \from[dameng].

\page
\blank[5cm]
\subject{特别感谢:无序}
\startitemize[n]
\item 感谢老妈老爸的支持.
\item 感谢\from[liyanrui],这个小册子的撰写采用了他的那个Hello \ConTeXt的模板
\item 感谢最先给\from[ooc]这个项目反馈的\from[ekd123]以及促使我写这个小册子的\from[qkevin]
\item 感谢\from[fleurer]关于文字方面的反馈
\item 感谢\from[wuhao]的支持.
\item 感谢\from[liyanrui]在发布协议方面的提醒.
\item 感谢\from[qkevin]在其博客上开通专栏介绍\from[ooc]
\stopitemize


\page
\blank[5cm]
\subject{一些说明:倒序}
\startitemize[n]
\item 重写第一章和第三章的相当一部分内容,概念描述更加明晰
\item 删除部分过激的文字,尽管有些是出于调侃,要谦虚,不能膨胀
\item 文档方面开始使用GFDL协议发布
\item
\from[fleurer]的来信中指出了诸多不足,特别是OO的概念方面.主要是某些细节上我的说法与一些常规的说法不太统一,
当然对于有些抽象概念性的东西,目前觉得还是自己的说法好,只是有待更好的表达.
所以准备在第一章在加一些东西,同时统一后面的概念性名词.
\item 决定试用\ConTeXt 来编写本文档,一方面为了学习下\ConTeXt 另一方面也为了方便日后的维护与修改.
\item 其实一直想写些和OOC相关的文档,不过只是偶尔在博客里零零碎碎的写过几篇,一直没有时间把这些总结一下.前几天在\from[qkevin]那里看到一篇相关的文章,留言中把\from[ooc]这个项目的地址放了上去,得到的反馈是还是太复杂了(即使是核心宏).
于是下定决心开始写文档,争取以更简单的方式把\from[ooc]的设计思路展现给大家,同时也期待更多人参与进来折腾.
\stopitemize

\chapter{热身运动}
\section{什么是OOC}
注意,本节是后来增加的,主要是为了告诉大家什么是OOC,以及作为一个OOC的\quotation{框架},到底应该有些什么.

OOC即Object Oritened C,当然也有些人称之为OOPC,多出来的P指Programming.而OO指面向对象,当然有OB(Object Based)一说,就是基于对象,在这两个名词之间不多啰嗦,一句话概括的化,OO比OB更\quotation{纯}一些,尽管这样概括并不够准确.

本手册所说的面向对象的C主要涉及两个方面,风格与简化!
\subject{OOC涉及的两个主要方面}
\startitemize[n]
\item 采用何种面向对象的编码风格
\item 如何简化因为使用某种编码风格而产生的额外代码
\stopitemize
第一方面不同的人会有不同的见解,虽然千差万别,但却大同小异.
第二方面主要的实现机制还有两种,一种是通过独立的预处理程序来生成额外的代码,另一种是用C语言本身支持的宏特性来简化.
前者可以让代码获得更多的特性,后者可能无法很好的简化部分因为OO风格而产生的冗余代码,但是使用起来却更加方便.


当然准确一些的话OOC还应该有一个较为完整的类型系统,比如GObject库这方面做的比较全,但是个人认为其前两方面做得不怎么样,所以用纯C开发起来并不方便.

本手册主要是围绕一个名为\from[ooc]的小项目展开的,自然介绍的是其中采用的OO风格,而这个项目本身针对更多是纯C的开发,
并且在使用上尽力做到简易,所以采用了宏来完成\quotation{偷懒}的工作.
 
\section{简单梳理一下OO的常规概念}
无论过程式与对象式的编程,我们总会涉及到两方面的问题\quotation{数据封装}与\quotation{数据抉择}.
一个比较夸张点的说法就是面向对象无非是打肿脸充胖子,通过一些概念和简化手段,
让\quotation{封装}与\quotation{抉择}这两块的具体代码看上去相对美观了些.

\subject{面向对象中常涉及的概念}
\startitemize[n]
\item 类,成员,接口与方法
\item 包,模块,命名空间,访问权限
\item 重载,重写,继承,多态,泛型,反射与自省
\stopitemize


上面写的东西可能不全面,但即使这些展开说也多了去了,而且具体到某些细节我的理解也不一定全都正确.
所以一来为了简洁,二来为了不去一不小心误人子弟.这些东西我争取用一两句话来介绍.而因为本手册是针对C来使用的,也会给出一些在C语言里的对应.

类及类成员是啥我就不多说了,就像C中的结构与结构成员.接口与方法到C里面都指函数.tmd也不知哪个龟孙开始提出的OO,整出这么多概念来.
C语言常用结构体来模拟类,自然普通结构成员就对应类成员,而类的方法采用函数指针来模拟,个人的理解是这种指针的模拟都对应接口或是虚方法.
当我们把某个函数指针指向具体的函数,就相当于真正的实现了某个接口或是将虚方法化虚为实.

C语言里没有什么包,命名空间等这类东西,不过模块化的C一般就是采用独立文件的编写方式.
常把一个模块放在一个.c和一个.h文件中,不同的模块经过编译产生了众多的.o文件,再通过链接将各个.o统一起来.
关于访问权限,一般的OO语言常涉及public,private,protected这些,主要是限制是类中访问与类外访问以及继承时的权限.
C语言可没这么多条条框框,大体上除了局部括号内受限以及static关键字限定某些文件内访问.其它的都很自由,你自己掌控一切.

关于继承,我见过两种描述,当然个人觉得这两种说法并不冲突,只是侧重点不太相同.
一种感觉重在描述\quotation{数据抉择}这一块,就是如何去使用数据.
其说法是子类使用父类的方法这是继承,而子类实现父类的接口在\quotation{向上转型}成父类再来使用以获得不同的实现这是多态(的一种体现).
另一种关于继承的解释重在\quotation{数据封装},就是继承就是子类包含父类的东西,从本之上说是一种包含关系.

个人比较喜欢从\quotation{数据封装}方面来阐述继承,因为这更和字面意思,和现实生活也更加对应.比如儿子继承母亲的某些性格特征,
继承遗产等等,侧重的也是\quotation{数据封装}.当然现实中的继承远比程序中的复杂,而且很多现实继承是那种父类大,子类小,子类局部继承的情况.
而程序中的继承基本都是由小到大的模式,这就要求我们进一步分析并采用一些模式来完成数据的抽象.

从\quotation{数据封装}方面来看代码中的继承,大体也有两种.一种是一般成员继承,
这种就是前面从\quotation{数据抉择}上的通过子类使用父类,另一种是接口等的继承,这种具体实现是在子类中完成的,
但使用时以父类的形式使用,如前所述\quotation{数据抉择}上通过父类使用子类的情形,一个父类定义接口,有很多子类的不同实现,这就体现了多态这一概念.

用C语言来完成\quotation{数据封装}上的继承,一种常规方式是子类结构体包含父类结构体的形式.并且把父类结构体放在子类结构体的首部,
这样做的一个好处是当我们在堆上分配一个对象,只需强制转换其指针到父类指针形态,就可以按照父类来使用了,当然这种使用本身看上去是通过父类使用子类,也就是多态(的一种).

下面再下重载重写等相关的定义好,这一块我直接用英文了(因为我有些整体相当不错的书的中译这一块翻译上总有差异),
就是overload,override,overwrite(还有人有时还会用到一个词overlap).关于这一块我想多说几句,就是关于这几个词不同语言上也可能有一些细微的差异.
而且很多所谓的OO语言对这一块虽然支持,但并不完美,尤其体现在overload运算符以及返回值的处理的支持上.
简单说overload是允许某类中名称相同但参数等不同的方法的存在,这需要编译器或解释器能够通过传入参数等进行判断进而确定到底是用哪个方法.
override和overwrite都是子类中有和父类重名的方法,不同的是overwrite会抹杀掉父类的方法,而override只是隐藏了父类的方法.
从C语言来说,因为方法都是用函数指针来模拟,所以这些都可以模拟,但是都不是自动的,需要相当的额外代码来辅助,甚至是需要构件一个抽象类作为中间层来模拟.

和overXXXX系的对比着可以说一下的就是泛型,比如overload是针对的是几个不同参数的方法,让编译器根据参数等来判断.泛型通常是只写一个方法,但是参数采用抽象的形式,
编译器通过类型推衍等来判断具体是什么数据(当然这么概括泛型是为了简便,可能不太全面).

最后说一下反射(reflection)和自省(introspection),这是更加动态的机制,可能需要类型系统等的支持.这两个概念有些人分不清,也有些人干脆把他们揉在一起来理解,不过这也不影响,只要知道其动态特性怎么用就好.
简单的说反射式通过一个动态的字符串,获得相应的类或方法等.自省则是通过一个已有的但未知类型的实例直接获得其相关信息或是直接使用.C99好像支持了__TYPEOF__(gnu套件一般用小写的typeof),有点自省的意思了.
但是去模拟反射却不容易,需要额外的数据结构以及约定好的类型系统库才行(个人觉得).还有要说的是反射和自省看似美丽但是用起来代价也是不小的,比如额外引入的数据处理对性能影响比较大,
很多时候使用这些机制会将代码变得冗长难懂,类型不安全等.

广义上讲多态就是只从多种数据中选择需要的适合的来用,从这个角度来说它与前面说的\quotation{两大部分}中\quotation{数据抉择}部分直接对应了.这样理解的话,它就包含了前面说的overload,泛型,反射,自省等等.
而广义上的继承则也占据了\quotation{数据封装}中的相当一部分.因此某种意义上说广义上的继承和广义的多态就是OO的核心(不要动不动就下定义,所以我加了个限定词\quotation{某种意义上})

用C去模拟OO,其实无论\quotation{数据封装}或是\quotation{数据抉择}哪一部分都能模拟,只是模拟的时候额外的代码量也是相当可观的.
而单从用宏来简化这些额外代码上,主要能简化的就是\quotation{数据封装}的部分,\quotation{数据抉择}部分如果想要简化最好还是通过间接代码生成的方式.
也因此\from[ooc]中的宏主要是在解决\quotation{数据封装}方面的问题,对于\quotation{数据抉择}方面的OO模拟还是要写不少额外的代码.

\subject{注意}
上面的概念可能有不少地方不够准确或是存在错误,如果有所发现,还请联系我\from[dameng].
另外后面的章节尽量不再解释和OO相关的抽象概念,如有需要,请参阅此节.
 
 
\section{为什么不用CPP?}
CPP过于冗杂,别准不够统一.编译器干了太多的活,想弄明白需要相当的时间去折腾,而想弄精则更难,学习曲线既长又陡.
而且很多人因为入门时拜师不慎(比如\quotation{邪恶的M}),把C和C++混为一谈,而忽略了C++的复杂性,另外这导致了相当一部人拿着C++来写C的代码!
这本没错,只是你不觉得这很另类很诡异么?!!!

我个人罗列几个问题,有些并不难,有的甚至都不算严格的问题,只是一些概念.但我相信只学了两三个月的C++新手,总有没听说过或是不会的.
\startitemize[n]
\item 引用和指针有哪些区别
\item 通过哪种方式可以屏蔽C++中默认构造函数的使用
\item 大陆编程书籍中常出现的接口这个概念,C++中有借口么或是有相关对应么,怎么用?
\item 友元这一概念存在的意义以及如何使用
\item C++结构体和类相比有哪些限制,可以在C++结构体中定义函数,静态函数,虚函数么?
\item C++重载是否适用于使用同名同参数但不同返回值的情形.
\item 解释虚表虚函数的概念
\item C++中auto_ptr(只能指针)是怎么回事儿,有何作用,怎么用?!
\item C++中多重继承如何避免名称冲突
\item 解释下隐式类型转换
\item C++可以用哪些类型转换措施,有静态动态之分么,向上向下都允许么,如果有限制硬是使用会产生哪类问题.
\item 我想定义个函数指针指向某类中的一个方法,怎么做?如果是指向类中的一个虚方法或是静态方法,有却别么?
\item virtual和rtti有何关系
\item 如何使用c++中的rtti
\item 类中的函数,虚函数,静态函数是按类来分配还是按实例来分配.
\item 类中的函数,虚函数,静态函数的具体内存分布大致是什么样的,先后顺序如何.
\item 不同类的函数,虚函数,静态函数是在统一的一大块区域中分配,还是离散的毫无关联的.
\item 如何获得一个类的虚表指针.
\item 如何重载或是能否重载+,++,<<,=,==这些运算符,重载时有限制么?
\item 如何或是能否声明一个函数指针,其参数或返回值有模板参数.
\item rtti的使用有何副作用
\item ..bla..bla..
\stopitemize
这里面大部分问题我还是知道或是有印象的,有一小部分我也比较好奇.不过上面的问题还不涉及C语法中相对复杂的东西(比如复杂指针的使用).
总之想真正掌握C已经不容易了,再要引入这么多东西真是有点吃饱撑的感觉了,总之我表示我的脑容量有限,估计从一点不知道开始学,
要花很长时间来学,而且如果用的不多或是一段时间不用,可能又忘了,可能细节就往干了,可能要从头再来.人生苦短,如果你真想学的
又快又好又高级的语言个人建议你还是用python吧.如果你要编译后的高效,不妨试试go语言.或者仅仅学习C语言,再学习下这份简单的
OOC教程.


\blank[3cm]
在开始下一节前说一下,本手册\quotation{热身运动}一节介绍了一种简单且对称的OO风格,
\quotation{伸展运动}介绍了一些宏来简化这一部分的代码.
当然这两节中所涉及的都比较简单,主要是为了便于大家的理解和使用.后面的章节则会循序渐进的加强这些代码!
从下节开始真正就有代码了,如果你是从头看到这里,相信你已经受够我的啰嗦了.

\section{一个结构体+一个函数?!}
\starttyping[option=c]
#include <stdio.h>
struct A {
	int a;
};
void iniA(struct A *THIS){
	THIS->a=100;
}
int main(){
	struct A obj;
	iniA(&obj);
	printf("the value of obj's a is %d\n",obj.a);
	return 0;
}
\stoptyping
这个代码?干啥的?

这个例子很是简单,只涉及一个结构体的定义和一个相关初始化函数,但在这份手册中却有着非同一般的意义,而一切用C去模拟OO的东西也源于此.

所谓面向对象的编程,其本质目的是把数据一层一层封装起来从而使代码看上去更加易懂,更易维护.单从封装数据的角度来看,C语言中的结构体足矣.而封装之后如何使用它,自然要涉及所谓的构造函数,对应到上面的代码,就是那个名为iniA的函数啦.
\section{不得不说的函数指针}
前面的代码似乎过于简单了,肯定会有人说这不是面向对象的编程,从个人观点来看,面向对象只是一种思想,而这种思想也主要用在数据的封装上,而上面的代码其实已经体现了一点点.而下面将要做的就是一点点对其加强.
\starttyping[option=c]
#include <stdio.h>
struct A {
	int a;
	void (*showA)(struct A *);
};
static void A_showA(struct A *THIS){
	printf("the value of obj's a is %d\n",THIS->a);
}
void iniA(struct A *THIS){
	THIS->a=100;
	THIS->showA=A_showA;
}
int main(){
	struct A obj;
	iniA(&obj);
	obj.showA(&obj);
	return 0;
}
\stoptyping
C语言中没有C++所谓的\quotation{方法},其实也没有这个必要,因为本质上那就是一个函数.还有些语言只有所谓的\quotation{过程},他们本质上都是一样的(后面的文字不再区分这三个概念,如遇到都理解成C语言中的函数就好,同样后面关于类和结构的称呼只要是在C语言中本质也是一样的),只是细节上有些许差异.
另外要说的是C语言的结构体是可以包含函数指针的,这一特性也使得用C去模拟对象变得可行且有意义.比如上面的代码就演示如何使用\quotation{封装}在结构体重的函数指针.

\section{栈内存vs堆内存}
指针对于初学C的人来说是个难点,而更要命的是堆内存和栈内存的问题(如果你仍不清楚这个问题,那么先回去补一补C语言的基础,我在这里不在赘述,因为这一块是会者不难,但是如果不会讲起来可啰嗦了).
个人认为国内学生初学C语言时容易犯这样的错误很大的一个原因就是入门教材一直用的是国内最流行的那本...

对于模拟OO的编程,分清堆内存和栈内存是很有必要的,特别是堆内存的使用会使你的程序具备一定的\quotation{动态}特性.
但使用堆内存有利也有弊,特别对于C乃至C++这样的语言来说,使用不当就杯具了,内存泄露这样的问题也源于此.下面再看一段代码
\starttyping[option=c]
#include <stdio.h>
#include <stdlib.h>
struct A {
	int a;
	void (*showA)(struct A *);
};
static void A_showA(struct A *THIS){
	printf("the value of obj's a is %d\n",THIS->a);
}
void iniA(struct A *THIS){
	THIS->a=100;
	THIS->showA=A_showA;
}
struct A * newA(){
	struct A * mem=(struct A*)malloc(sizeof(struct A));
	iniA(mem);
	return mem;
}
int main(){
	struct A * obj=newA();
	obj->showA(obj);
	free(obj);
	return 0;
}
\stoptyping
这里要说明一下,iniA和newA这两个函数是为了模拟构造函数,本质上iniA进行真正的初始化,newA实际上在外边又封装了一次,在堆内存上分配这个结构体.
这两个函数让我们可以按需在堆上(newA)或栈上(iniA)声明一个结构实例,其实也就是本手册要模拟的所谓的\quotation{类}.

值得注意的是上面的代码有一点很不爽的地方就是最后还要free一下堆内存,而与之对应的malloc则放在了newA这个函数中,
这样的代码让我很是不爽,所以下面继续完善,引入\quotation{析构}函数的模拟.

\section{对称之美}
在C++中有构造也有析构,只是编译器替我们做了太多的工作,以致有些初学者对此毫不知情.这是我反感C++的原因之一,它掩饰了太多的东西,虽然有时看上去简单了,
但如果你不了解底层,很多东西会觉得很不明晰,也因此这是门初学更容易犯错的语言.
 
下面要说的就是析构函数的模拟了.有了构造,再有析构,代码才会有对称性,至少看上去才更加的OO.
\starttyping[option=c]
#include <stdio.h>
#include <stdlib.h>
struct A {
	int a;
	void (*showA)(struct A *);
};
static void A_showA(struct A *THIS){
	printf("the value of obj's a is %d\n",THIS->a);
}
void iniA(struct A *THIS){
	THIS->a=100;
	THIS->showA=A_showA;
}
struct A * newA(){
	struct A * mem=(struct A*)malloc(sizeof(struct A));
	iniA(mem);
	return mem;
}
void finA(struct A *THIS){
	THIS->a=0;
	THIS->showA=NULL;
}
void delA(struct A **THIS){
	finA(*THIS);
	free(*THIS);
	(*THIS)=NULL;
}

int main(){
	struct A * obj=newA();
	obj->showA(obj);
	delA(&obj);
	printf("is obj NULL ?\n%s\n",obj==NULL?"True":"False");
	return 0;
}
\stoptyping
finA函数比较简单,其对应构造用函数iniA,是真正的析构部分.
而delA则对应newA,仅仅是又把finA再次封装了一下,但要注意的是这个例子中用到了指向指针的指针(其实也可以普通的指针),
这样做有一个好处,比如上面的代码,在delA执行之后,obj已经指向NULL了,如果我们再次使用已经执行过析构的obj对象,则错误会比较明显.
最后的那个打印函数也是为了验证obj释放后置零这一特性.

现在趁热打铁,总结一下本手册以后常用的几个重要函数(假定我们有一个类名曰Class),下面为了加强记忆放在一起总结一下.
\startitemize[n]
\item void iniClass(Class *THIS);
→→ 用于栈上对象的构造 , ini作为一个prefix(前缀),表示初始的意思,相关词汇initialization,initiate,initial
\item void finClass(Class *THIS);
→→ 用于栈上对象的析构 , fin作为一个prefix(前缀),表示终止的意思,相关词汇finish,final
\item Class * newClass(void);
→→ 用于堆上对象的构造,是iniClass的封装 ,
new作为一个prefix(前缀),表示新建的意思,相关词汇new,neo-系部分词汇,在某些语言中直接对应new这个KeyWord
\item void delClass(Class **THIS);
→→ 用于堆上对象的析构,是finClass的封装
del作为一个prefix(前缀),表示删除的意思,相关词汇delete,de-系部分词汇,在某些语言中直接对应delete这个KeyWord
\stopitemize

\section{继承与多态}
在C++的编程中关于\quotation{数据封装}有两种关系很是重要,一是继承关系,另一是包含关系.本质上其实一样的,都是\quotation{包含},
只不过C++的编译器再度不辞劳苦的帮我们做了点工作.让所谓的继承关系使用起来似乎容易了些.

C语言本身没有继承方面的语法糖,但是包含关系应该是所有的计算机语言都能描述的,因为汇编都可以,其它更高级的语言自然也能描述.
在C语言中,一种常见的模拟继承的做法是把父类放在子类的首部.
具体见下面的代码.
\starttyping[option=c]
#include <stdio.h>
#include <stdlib.h>
struct A {
	int a;
	void (*show)(void *);
};
static void A_showA(struct A *THIS){
	printf("the value of obj's a is %d\n",THIS->a);
}
void iniA(struct A *THIS){
	THIS->a=100;
	THIS->show=(void *)A_showA;
}
struct B {
	struct A A;
	int b;
};
static void B_showB(struct B *THIS){
	printf("the value of obj's a is %d\n"
			"the value of obj's b is %d\n",
			THIS->A.a,THIS->b);
}
void iniB(struct B *THIS){
	iniA((struct A*)THIS);
	THIS->b=200;
	((struct A*)THIS)->show=(void *)B_showB;
}
int main(){
	struct B obj;
	iniB(&obj);
	struct A *s=&(obj.A);
	s->show(&obj);
	return 0;
}
\stoptyping
因为上面这段代码只使用了栈内存,而对象本身也没有指向堆内存的指针,所以为了简便只保留了\quotation{四大函数}中的ini系列.
同时也有一些细节上的修改,比如A中的函数showA改为show,这是为了演示如何在子类中重写父类的函数.

我们假定子类B继承了父类A,自然用C的包含关系去模拟继承关系时应将父类放在首部(当然也有些特殊应用要求统一放在尾部),这样做的一个好处是方便指针型的强制转换.
而在使用的时候这种子类指针型强制转型成父类函数指针型的做法一般被称为\quotation{向上转型},在使用的时候我们用的其实是一个父类指针,这样的\quotation{数据抉择}
体现了\quotation{多态}的思想.在父类中定义方法(接口),子类中具体实现.使用时则通过父类的形式来调用.


\section{贴心的匿名结构体}
上一段代码相比会让大家觉得有些丑陋,因为用到了不少强制转换.其实现代的主流C编译器都支持匿名结构体(anonymous struct或unamed struct)这一特性.这样用C去模拟OO的继承关系时就更加舒服了.下面的代码和上一段代码功能完全相同,但得益于匿名结构体这一特性,看上去更加悦目了.
\starttyping[option=c]
#include <stdio.h>
#include <stdlib.h>
struct A {
	int a;
	void (*show)(void *);
};
static void A_showA(struct A *THIS){
	printf("the value of obj's a is %d\n",THIS->a);
}
void iniA(struct A *THIS){
	THIS->a=100;
	THIS->show=(void *)A_showA;
}
struct B {
	struct A;
	int b;
};
static void B_showB(struct B *THIS){
	printf("the value of obj's a is %d\n"
			"the value of obj's b is %d\n",
			THIS->a,THIS->b);
}
void iniB(struct B *THIS){
	iniA((struct A*)THIS);
	THIS->b=200;
	THIS->show=(void *)B_showB;
}
int main(){
	struct B obj;
	iniB(&obj);
	obj.show(&obj);
	return 0;
}
\stoptyping
匿名结构体的使用可以让我们省去不少强制转换的麻烦.但是,有一个很大的问题就是当有重名成员时到底如何处理,是直接报错,
还是不额外多分配重名成员所占的内存,还是额外分配重名成员所占的内存.关于这个问题GCC4.5和GCC 4.6的处理方式多少有些不同,
具体可以参见GCC testsuite中关于anonymous struct的部分.

这里多啰嗦几句,GCC原生支持的如下这种匿名结构体,暂用Type A简记
\starttyping[option=c]
stuct B{
    struct A{
        int a;
    };
    int b;
};
\stoptyping
注意A是在B中声明的

而据GCC官方文档所说,M\$的编译器则原生支持如下形式的,暂用Type B简记
\starttyping[option=c]
struct A{
    int a;
};
stuct B{
    struct A;
    int b;
};
\stoptyping
如果想让GCC支持Type B这种类型的匿名结构体,4.5版本在编译时须加上-fms-extensions,
而4.6版本则加上-fplan9-extensions,比如上面的代码如果要用GCC编译,就应加上这些选项.

\section{休息一下}
这一节,仅仅是回顾与总结

个人认为前面已经大致说清了OO思想中关于\quotation{数据封装}部分最为重要一些东西.

而要用C来模拟OO,首先要解决则是如下的一些问题
\startitemize[n]
\item 结构定义
\item 构造方法
\item 析构方法
\item 包含关系
\stopitemize
\blank[3cm]

如果你逐行的看完前面的代码,一定会觉得很累,虽然他们实现的功能很简单,仅仅是打印一两个数值而已.由此看来上面的工作似乎时间扯淡无比的事情啊!!!

没错,上面的代码的确无比扯淡,很适合我们吃饱了撑着的时候去研究,也省的吃吗丁啉了.但是,这种一层层的封装本身还是很有必要的,
只是我们不应重复的去敲这么多的代码,也不应去记忆那么多繁琐的细节.

值得庆幸的是C语言是支持宏定义的,想想<<电子世界争霸战>>中那些华丽的nested macros!但是程序毕竟不是时装秀,宏的滥用也绝不是件好事.
也因此很多更为高级的语言抛弃了这一可能带来灾难错误的特性.但是不可否认,一旦某些宏称为一种约定,则是既简洁有好用的.

在下面的章节中,我会使用三个宏CLASS,CTOR(对应CONSTRUCTOR)和DTOR(对应DESTRUCTOR),通过它们来完成类本身的设计,
以及构造函数和析构函数的设计,进而简化上面的OO风格所带来的冗余代码.
如果把这三个宏展开来看,和本章的代码并无差异.


\chapter{伸展运动}

\section{CLASS,CTOR,DTOR}
宏是一个十分强大的特性,某人说通过宏实际上我们可以创立一个新的语言.
当然个人认为只是形式上罢了.不过通过宏,代码看起来真的可以很简洁,很强大.
比如下面所介绍的,将用一些简单的宏让C代码看上去有点C++的味道.
\starttyping[option=c]
#include "OOC.h"
CLASS(A){
	int a;
	void (*showA)(A *);
};
static void A_showA(A *THIS){
	printf("the value of obj's a is %d\n",THIS->a);
}
CTOR(A){
	THIS->a=100;
	THIS->showA=A_showA;
}
DTOR(A){
	THIS->a=0;
	THIS->showA=NULL;
}
int main(){
	A * obj=newA();
	obj->showA(obj);
	delA(&obj);
	return 0;
}
\stoptyping
这个例子中用到了三个宏,分别是CLASS,CTOR(对应CONSTRUCTOR),DTOR(对应DESTRUCTOR).
如果把他们展开,其实和前面\quotation{对称之美}一节中的代码是一样的,但是因为使用了宏,可读性大大提高,
而且代码量减少了大概 $\frac{1}{3}$ .这只是一个简单的类的模拟,如果类比较多且继生关系比较复杂,
那么节省的代码量还是相当可观的.

不过这里还是要再次强调一下,宏是好东西,但如果滥用反而会大大降低可读性,比如注明的C类型系统库GObject,
如果我们去剖析其源码,那叫一个恶心啊!!!

闲话少扯,现在具体来看一下CLASS,CTOR和DTOR宏具体是怎么实现的
\starttyping[option=c]
#include <stdio.h>
#include <stdlib.h>
#define CLASS(Type) \
typedef struct _##Type Type; \
void ini##Type(struct _##Type *); \
void fin##Type(struct _##Type *); \
Type * new##Type(); \
void del##Type(struct _##Type **); \
struct _##Type

#define CTOR(Type) \
Type * new##Type() { \
    Type *THIS; \
    THIS=(Type*)calloc(1,sizeof(Type)); \
    if( NULL==THIS ) { \
        return NULL; \
    } \
    ini##Type(THIS); \
    return THIS; \
} \
void ini##Type(Type * THIS)

#define DTOR(Type) \
void del##Type(Type **THIS) { \
    fin##Type(*(THIS)); \
    free(*(THIS)); \
    (*(THIS))=NULL; \
} \
void fin##Type(Type *THIS)
\stoptyping
没错,就是这么简单!

下一节将会闲扯一些为什么这样设计,以及仅仅是这样还有那些不足.

\section{从CLASS说起}
关于为什么这样设计,先从CLASS宏说起,先回顾一下,
\starttyping[option=c]
#define CLASS(Type) \
typedef struct _##Type Type; \
void ini##Type(struct _##Type *); \
void fin##Type(struct _##Type *); \
Type * new##Type(); \
void del##Type(struct _##Type **); \
struct _##Type
\stoptyping
C语言中直接使用结构体其实是件很麻烦的事,因为每次声明都要使用额外的struct!,而是用上面的宏,就可以\quotation{偷懒}啦!

说到这一点,多啰嗦几句.一些初学C的人,特别从VC开始入门那些,总会分不清什么是C,什么是C++,
因为M\$的编译器把他们混在一起了,据说会根据文件名后缀来区分.比较容易混淆的错误之一就是struct实例的声明,
真正的纯C编译器如果不事先typedef一下是必须加个struct的.
而另外一个易混淆的初级错误就是C语言中到底有没有try...catch机制.
记得上学时看一本计算机2级C的官方教材,竟然有异常处理一章,
而且讲的是C++中的try...catch(本手册后面后面会介绍关于用纯C模拟try...catch的一种做法),
估计出书的人也是老谭的学生吧.

言归正传,一种常见的做法是当我们定义结构体时,用typedef来声明一下,比如我们要定义一个结构体A,
如果像下面的做法,使用时实例的声明可以直接像\quotation{A ...;}这样写啦!

\starttyping[option=c]
typedef struct {int a;} A;
int main(){
	A obj;
	ojb.a=1;
	return 0;
}
\stoptyping
上面的这一小段代码中的typedef写在一行是为了说明typedef的语法,
我们一般会使用如\quotation{typedef int Boolean;}这样的定义,
但是有些初学者会对typedef配合结构体的语法感到诧异,如果我们像上面这样写就会清晰多了,
仿佛就是把一个匿名的结构类型重定义成一个新的类型.而CLASS宏中的写法其实也是类似,
只是匿名的结构有一个显示的以下划线打头的名字,其相当于下面的代码.
\starttyping[option=c]
typedef struct _A A;
struct _A{
	int a;
};
int main(){
	A obj;
	ojb.a=1;
	return 0;
}
\stoptyping

另外在CLASS宏中还预先声明了第一章中所声明的\quotation{四大函数},声明放在最前面是有好处的,
它会让编译器在一开始就知道有这么些类型的函数.
而CLASS宏的最后则是一个开放的形式(这里的全开放是指某个宏不需要语气配合相对应的截止宏来保卫一段代码),
我们自己要补上\quotation{\{+成员;+\}[可选的全局实例];}

\section{CTOR&DTOR}
这一节来说一下用于模拟构造函数的宏CTOR以及用于模拟析构函数的宏DTOR.先回顾下CTOR的定义.
\starttyping[option=c]
#define CTOR(Type) \
Type * new##Type() { \
    Type *THIS; \
    THIS=(Type*)calloc(1,sizeof(Type)); \
    if( NULL==THIS ) { \
        return NULL; \
    } \
    ini##Type(THIS); \
    return THIS; \
} \
void ini##Type(Type * THIS)
\stoptyping
在上一章中,我们说过那个newA函数仅仅是对iniA函数的一个封装,
同时也不难发现其实现也是有一定规律可循的,
有规可循的东西用宏把它标准化是一件再好不过的事.
所以CTOR宏在一开始就直接实现了new\#\#Type函数,具体到某个类A,
也就是newA函数.而后面的ini\#\#Type函数则要我们自己去实现,
因为这才是构造的核心,如果所有的类这个都有人帮我们做,那还要程序员干嘛?!

和CLASS宏一样,CTOR宏也是全开放式的.但是CTOR宏的全开放却来之不易,之所以能做到全开放,
和前面的设计很有关系.因为我们将涉及栈内存和堆内存的功能分别放在两个函数(ini\#\#Type函数和new\#\#Type函数)之中.
关于这一点大家也可以参考一本国内能买到讲OOPC的书<<UML+OOPC嵌入式C语言开发精讲>>[高焕堂 著],
那本书里介绍的OOPC就是我这里说的OOC,也是用宏来简化一些东西,去模拟类似C++中的OO机制.
不过大家看过之后会发现那个里面有构造却没有析构,而且像构造函数这样的宏也不是开放性的,
需要配合特殊的结束宏END_CTOR,应该说那本书中所介绍的宏对OO的模拟式很不完善的(不过关于采用OO方式C变成本身来说还是
可圈可点的,还有UML等的相关介绍,不过就是设计本身个人感觉一般).


说起相关的OOC的书籍,这里再补充一些以供大家参考,
\startitemize[n]
\item 一本非常值得推荐的书是David R. Hanson写的那本<<C Interfaces \& Implematations>>,
这本书并没有明说OOC的概念,但其实本质都一样,也就是如何去封装,如何去写模块化的C语言.

\item 另一本是<<Object-Oriented Programming with ANSI-C>>这本书更多介绍的是用C去进行OO编程的方式与思想,
所以前面的章节写的比较抽象.而后面的章节为了简化那些繁琐的工作(比如本手册第一章涉及的一些),其引入了一个预处理程序,
有点类似Qt的Moc.当然个人的观点是既然有了额外的预处理的步骤,就不能算是纯粹的C或C++了,
也因此部分Qt程序员觉得其实moc的存在也是一种遗憾.

\item 另外就是也可以参考些Gtk的基石--GObject库,这个库是以OO的思想设计的C语言类库.
但是个人的观点是它更多的是在做一个类型系统,更多的是为了方便高级OO语言的绑定.
也因此,这个库在用纯C来开发时似乎并不是很容易.繁琐的宏定义时常会让人摸不着头脑.
具体可参阅GNOME官方网站,以及那本<<Foundations of Gtk+>>

\item 还有就是李先静写的那本<<系统程序员成长手册>>,写的也不错,里面模块化的设计风格实质也是讲这些的.
\stopitemize

下面再来看一下用于析构的DTOR宏,
\starttyping[option=c]
#define DTOR(Type) \
void del##Type(Type **THIS) { \
    fin##Type(*(THIS)); \
    free(*(THIS)); \
    (*(THIS))=NULL; \
} \
void fin##Type(Type *THIS)
\stoptyping
和CTOR宏一样也是全开放的宏,我们只需要去设计fin函数中的内容而del函数则会自动生成.

\section{从男孩到男人}
不要被本节的标题吓到了,只是本节的例子仅仅是用CLASS,CTOR以及DTOR宏去完成一个Kid类和一个Man类,
具体还是直接看代码吧,为了方便这段代码分段来看
\starttyping[option=c]
#include "OOC.h"
typedef int bool;
typedef unsigned int uint;
CLASS(Human){
	bool gender;
	uint age;
	void (*say)(void *);
};
CLASS(Kid){
	Human Human;
};
CLASS(Man){
	Kid Kid;
	uint iq;
};
\stoptyping
上面这段是类的设计,假定以一个抽象接口类Human,两个普通类Kid和Man,
自然他们都是Human,所以对于Human他们都有包含(继承)关系,而Kid的包含是直接的,
Man则继承了Kid这个类(包含于首部).Human中三个成员分别是gender→\quotation{性别}属性,
age→\quotation{年龄}属性以及say→\quotation{说}方法.
Kid直接包含Human属性,没有额外的扩展,
只是简单的实现Human这个接口类,而Man则继承了Kid,
并新增了一个成员iq→\quotation{智商}属性.

\starttyping[option=c]
static void Kid_say(Human *THIS){
	printf("Kid's gender is %s\n",THIS->gender==1?"Male":"Female");
	printf("Kid's age is %d\n",THIS->age);
}
CTOR(Kid){
	Human *zs=(void *)THIS;
	zs->age=3;
	zs->gender=1;
	zs->say=(void *)Kid_say;
}
DTOR(Kid){}
static void Man_say(Human *THIS){
	Man *zs=(void *)THIS;
	printf("Man's gender is %s\n",THIS->gender==1?"Male":"Female");
	printf("Man's age is %d\n",THIS->age);
	printf("Man's iq is %d\n",zs->iq);
}
CTOR(Man){
	//iniKid(THIS);
	THIS->iq=130;
	Human *zs=(void *)THIS;
	zs->age=24;
	zs->gender=1;
	zs->say=(void *)Man_say;
}
DTOR(Man){}
\stoptyping
上面的代码是Kid和Man的具体实现,可以看到,抽象接口类Human并未出现在这段代码之中.因为根本也就没有这个必要,
因为他是一个抽象的.Kid和Man类在构造时各自都制定了自身的say方法,注意上面的代码中注释掉了Man构造函数中iniKid的调用,
规范化的代码应该保留的,这是在模拟继承关系.

这段代码中有很多void *的强制转换,这也是我要说的一点,某些教科书会告诫大家void*的强转时是单向的,但其实不然,
你可以亲自试一下,GCC来回转都可以,只要我们足够清除我们要做的事情,绝不用死搬教条.

关于析构函数,因为没有什么必要(主要是因为没有包含指向堆内存的指针),所以为了简单将其置空就好.

另外在此多说一点,目前这三个宏在本章的实现还很基础,比如对继承的模拟也是需要手工写额外的代码,
后面会介绍更多更好的实现,但相应的也复杂了些.

\starttyping[option=c]
int main(){
	Human *k=(void *)newKid();
	k->say(k);
	delKid((void *)&k);
	//
	Human * m=(void *)newMan();
	m->say(m);
	delMan((void *)&m);
	return 0;
}
\stoptyping

这是main函数调用的代码,正确的输出结果如下

Kid's gender is Male

Kid's age is 3

Man's gender is Male

Man's age is 24

Man's iq is 130

\section{轻松一下}
这一节依旧不涉及新的东西,只是做一些总结和思考.

这里用最简单的代码再复习一下,如何用上面的宏设计和实现一个模拟的类.这里还是假定一个类名为A,且只有一个整形元素a.
\starttyping[option=c]
CLASS(A){ int a; };
CTOR(A){ THIS->a=100; }
DTOR(A){}
\stoptyping

就是这么简单的事儿,但仅仅这样就某些应用来说似乎太简单了吧,比如像我前面所述继承方面的实现,
需要手动的去调用父类的构造(ini)和析构(fin)函数.除此之外,构造和析构函数也没有参数的传入,
一个好的构造应该是一个类似工厂模式的实现,并且这个工厂可以通过一定的参数产生不同的产品.
另外第二章中的实现也缺乏一些必要的错误检测机制.当然还有很多类似的不足有待我们去完善,
当然完善的同时宏本身的复杂度可能也会暴增.但是,有一个缺陷是最要命的也是最值得我们去克服的!这就是内存模型过于简单.

如果我们用过C++,会知道调试的时候观察某个类看到的只是成员而已,因为其方法是由编译器放在了另外的一个区域.
而我们现在的宏中如果我们的类的继承关系比较复杂,当我们去实现一个继承了很多类的实例,就必须分配很大的空间,
比如A类有50个方法(已经很多了),B类继承了A类,又扩展了另外50个方法,这样如果用上面这么简单的模拟方式,
每当我们生命一个B的实例,都要独立分配100个函数指针用来存储,对于某些小的应用可能无关紧要,
但是当我们类的继承关系非常繁杂的时候,这个缺陷可能是致命的!

其实对于某个类的方法以及同类型实例公共的成员变量,我们完全可以把他们放在单独的区域中,
也就是说方法要跟着类走而不是实例走!
但是如何实现这一部分确实一个问题,在保证简洁的同时还要是其使用起来方便.
我在后面的章节中将会介绍一种目前来说个人觉得不错的模拟方式,
这种方式在使用时每一个类型共享一块静态区域,但其本身对于某个类来说却是动态的(具备简单的引用计数特性).
这意味着使用时却无需增加额外的针对这一部分的内存管理用的代码,
因为这一部分内存的\quotation{分配}和\quotation{释放}是自动的!

\chapter{扩胸运动}
这一章之所以叫扩胸运动一方面与广播体操相对应,另一方面取\quotation{扩}字之意,意在对前文涉及的宏的扩展和加强!

\section{如何设计静态部分}
上一章节的结尾已经说到了和静态部分相关的东西.关于这一部分的重要性我不想再做重复.这里开门见山,
直接说一下我的一种简单的实现方式.

首先,我们要认真思考一下类的内存模型问题.原来的类就是一结构体,所以几乎不用多作考虑.而现在又要引入一部分东西,
如何引入就需要我们的创造型思维了.常规的做法是每个类对应一块\quotation{类区域},用来存放相对静态的部分.而每个实例有独自的
另一块\quotation{实例区域}.关于\quotation{类区域}一般的做法是,预先声明一块大区域,然后所有的类区域都放在这里.
这种做法可以做到真正意义的静态.但我下面要介绍的则采用的是一种相对动态一点的思路,在第一个实例生成是分配\quotation{类区域},
在最后一个实例销毁时再销毁该类的\quotation{类区域}.

除此之外,我们还要考虑如何设计宏来简化我们的代码.我们可以独立的去定义一个结构体,常规的做法也是这样的.
但是我总觉得这样用起来很不爽,因为看上去一个类就完全分离了!关于这方面,我采用了一种\quotation{插入宏}的形式,
原来的CLASS宏不变,不同的是当我们插入一句宏后,CLASS宏的前半部分用于设计实例部分的设计,后半部分用于\quotation{类区域}
的设计.另外我还要保证原CLASS宏的开放性!
 
\starttyping[option=c]
CLASS(A){ 
	int a; 
	SPLIT_MACRO ....
	void (*do_sth)();
};
\stoptyping 
按上面所述,大概CLASS宏使用起来就是这个样子.
SPLIT_MACRO宏用来分界前面为实例成员声明部分,后面的为类成员声明部分.

相应的在构造方面不妨也这样设想,比如
\starttyping[option=c]
CTOR(A){ 
	THIS->a=100; 
	SPLIT_MACRO_2 ....
	THIS->do_sth=real_do_sth;
};
DTOR(A){ 
	THIS->a=0; 
	SPLIT_MACRO_3 ....
	THIS->do_sth=NULL;
};
\stoptyping
和前面类似,其中的两个宏也是用来分界的.
SPLIT_MACRO_2前面为实例构造函数,后面的类构造函数.
SPLIT_MACRO_3前面为实例析构函数,后面的类析构函数.
这里要特别提醒一下,构造和析构中分界宏上下的THIS指针是不同的! 
 
那么具体如何实现呢?你可以自己想一下如何达到上面的设想,如果想不到也没关系,
因为下面我会给出的一个简单的实现,当然我还是希望读到本手册的人也能想一下,
或许更好的想法就会诞生,如果你想到了比下面说的更好的方法且不介意分享,
也请告诉我 \from[dameng]

\section{静态部分的简单实现}
基于上一节的一些约定和分析,这里不在过多啰嗦,直接给出具体的实现.
\starttyping[option=c]
#define CLASS_ST(Type) \
    struct _St##Type ** Type##_static; \
} ;\
CLASS(St##Type) {\
    unsigned int Type##_reference;

#define CTOR_ST(Type) \
    static struct _St##Type * Type##_static; \
    if(NULL==Type##_static) { \
        Type##_static=newSt##Type(); \
    } \
    THIS->Type##_static=&(Type##_static);\
    ++Type##_static->Type##_reference; \
} \
CTOR(St##Type)  {

#define DTOR_ST(Type) \
    St##Type * Type##_static=*(THIS->Type##_static); \
    if(0<Type##_static->Type##_reference) { \
        --Type##_static->Type##_reference; \
        if(0==Type##_static->Type##_reference) { \
        	*(THIS->Type##_static)=NULL; \
            delSt##Type(&Type##_static); \
        } \
    }\
} \
DTOR(St##Type) {
\stoptyping 

我想你的第一反应是\quotation{复杂},至少和前面的三个宏相比是这样的.特别是关于构造和析构的宏,太..!
其实我也想写的简单点的,可惜水平有限,在达成上一节所述的设计理念的前提下,这几乎是我能写出来的最简单的了.

管它到底如何呢?我还是会努力把这一块思路给理清楚.不过还是从简单静态类设计宏开始吧.

\section{静态部分的简单分析}
还是老样子先看类结构的定义部分

\starttyping[option=c]
#define CLASS_ST(Type) \
    struct _St##Type ** Type##_static; \
} ;\
CLASS(St##Type) {\
    unsigned int Type##_reference;
\stoptyping

如前所述前面的部分在实例结构体的尾部定义了一个双重指针型,用于指向静态结构体.
而静态结构体部分的有一个无符号整形,自然是用作计数只用.

可能前面的那个双重指针,初学者会比较迷茫.难道普通指针就不行么?
这点要完全解释清楚还是比较麻烦的,不过用双重指针的一个主要原因是为了配合引用计数机制,
为了在最后完成对一个构造函数中指针的清零!可能还有使用普通指针的方法,但至少目前我还没发现.

静态部分的内存是在堆上的,并且其内存的分配与释放,
要在其自身的构造与析构函数中完成而不是普通实例的构造与析构中完成.
最后的那个unsigned int型变量所起的作用就是引用计数!
每一个实例的生成都会对其+1,每一个实例的销毁都会对其-1!
当从0跳变至1时,静态部分自动产生,从1跳变0时,静态部分自动销毁,
对于普通开发者来说无需调用额外的方法去管理静态结构体的内存.
 
下面看一下静态构造宏
\starttyping[option=c]
#define CTOR_ST(Type) \
    static struct _St##Type * Type##_static; \
    if(NULL==Type##_static) { \
        Type##_static=newSt##Type(); \
    } \
    THIS->Type##_static=&(Type##_static);\
    ++Type##_static->Type##_reference; \
} \
CTOR(St##Type)  {
\stoptyping
和CLASS_ST一样,也是一个承上启下形式的宏,上边在做完实例构造之后还要判断是否生成静态实例.
如果已存在则不再而外生成,如果不存在则生成静态部分.最后再完成引用计数的+1工作!

而对于静态析构宏,则有些相似,
\starttyping[option=c]
#define DTOR_ST(Type) \
    St##Type * Type##_static=*(THIS->Type##_static); \
    if(0<Type##_static->Type##_reference) { \
        --Type##_static->Type##_reference; \
        if(0==Type##_static->Type##_reference) { \
        	*(THIS->Type##_static)=NULL; \
            delSt##Type(&Type##_static); \
        } \
    }\
} \
DTOR(St##Type) {
\stoptyping

无非是计数-1,以及判断什么时候销毁静态部分.值得注意的是\quotation{*(THIS->Type\#\#_static)=NULL;}
此句,前面静态设计宏CLASS_ST中用到的双重指针的意义就在于此,其要把一个实例构造函数中的指针置零,
才能让下一次实例生成时完成正确的判断!

如果你思考的足够深入,会发现一个问题,就是静态构造函数放在实例构造函数的尾部并不是见很好的事(放在首部会好的多),
但是如果那样就目前的宏而言又会失去一定的简洁度和自由度.这个问题我先不准备在这里解决,
在\from[ooc]中有较为完整的实现,如果需要可以直接看里面的源码,
其实现方式主要是在这里的构造与析构之中再增加一\quotation{层}.

\section{一个完整的带有静态部分的实例}
这个例子是对前面\quotation{从男孩到男人}那一节例子的修改,主要把\quotation{男人}类给砍掉了,
这样做自然是为了简单,因为这里重点要说的是如何使用静态部分,并且引入静态部分也会引入不少的代码.
下面来看下具体代码,放在这里的这是一个完整的,和前面的那个例子有些类似,只是改造了下,让其具备静态部分.
\starttyping[option=c]
#define USE_STATIC
#include "OOC.h"
typedef int bool;
typedef unsigned int uint;
CLASS(Human){
	bool gender;
	uint age;
	CLASS_ST(Human)
	void (*say)(void *);
};
CLASS(Kid){
	Human Human;
	CLASS_ST(Kid)
	StHuman StHuman;
};
static void Kid_say(Human *THIS){
	printf("Kid's gender is %s\n",THIS->gender==1?"Male":"Female");
	printf("Kid's age is %d\n",THIS->age);
}
CTOR(Kid){
	Human *zs=(void *)THIS;
	zs->age=3;
	zs->gender=1;
	CTOR_ST(Kid)
	StHuman *zs=(void *)THIS;
	zs->say=(void *)Kid_say;
}
//DTOR(Kid){DTOR_ST(Kid)}
DTOR(Kid){
	DTOR_ST(Kid)
	printf("after dtor of the StKid the reference count is %d\n",
			THIS->Kid_reference);
}
int main(){
	Kid *k1=newKid(),*k2=newKid();
	StHuman *sk=ST(k1);
	sk->say(k1);
	printf("reference count is %d\n",sk->Human_reference);
	delKid((void *)&k1);
	printf("reference count is %d\n",sk->Human_reference);
	delKid((void *)&k2);
	//printf("reference count is %d\n",sk->Human_reference);
	return 0;
}
\stoptyping
可以看到CLASS定义部分把那个say方法移至静态部分,也就是CLASS_ST宏的下边.
而Kid在继承Human接口时则要分两部分,一部是实例部分的继承,另一部是静态部分的继承,
这样做可能略显麻烦,但整体还是利大于弊的. 

和CLASS_ST宏一样,CTOR_ST和DTOR_ST宏的使用风格也比较统一,只要你看了我前面的啰嗦,很容易理解.

这里想说的是如何在main中使用,以及一些要注意的一些地方.

可以看到main中有这么一句
\starttyping[option=c]
StHuman *sk=ST(k1);
\stoptyping
这里平白无故的多出来了一个大写的ST,这是啥?!!

这个ST我前面一直放着没说,其作用是通过当前的实例结构体获得对应类的静态结构体指针!
注意是指针而不是实例中直接包含的双重指针,这样使用起来就方便多了.

下面是其具体的定义.
\starttyping[option=c]
#define ST(pointer) (*(void**)(*(void**)\ 
((void*)pointer+sizeof(*pointer)-sizeof(void**))))
\stoptyping
这里面用到了指针运算,不得不扯的是有些编译器不支持void*类型的指针运算的,
GCC对其的支持是按char*处理的,就是最基本的+/-.所以如果不支持,可以尝试把上面的void改成char.

另外ST宏要做的就是,
从一个指针获取一个其指向的结构体中一个双重指针指元素所指向的指针
(也就是最后这个指针指向结构体的地址),
这是句多么淡定的话啊!淡定到我不能肯定我的描述是正确的!
也正是因为这些原因,OOC的实施,为了简便,宏是必须的.

另外就是这个宏只接受一个参数就可以啦!这是很方便的事儿,但是却隐藏着极大的隐患.
当然这种隐患我们如果对其理解足够清晰是完全可以避免的.
这种隐患源于那个sizeof(*pointer),来看下面的错误的main函数调用.
\starttyping[option=c]
int main(){
	Human *k1=newKid(),*k2=newKid();
	SHuman *sk=ST(k1);
	sk->say(k1);
	printf("reference count is %d\n",sk->Human_reference);
	delKid((void *)&k1);
	printf("reference count is %d\n",sk->Human_reference);
	delKid((void *)&k2);
	//printf("reference count is %d\n",sk->Human_reference);
	return 0;
}
\stoptyping
注意main中第一行那个Kid,变为Human啦,这倒是和前面的\quotation{从男孩到男人}中的例子中一样了,
但确实一个严重的bug.因为那个ST通过sizeof计算便宜时会产生错误的偏移!
其实这也不算错误,因为一般的继承也是可以的,只是这类的父类是一个抽象类Human,
其并没有对其静态结构体的那个双重指针指针进行初始化!而对ST宏传入Human指针型,
会指向独立的StHuman结构,而不是StKid结构中所继承下来的StHuman结构,
而那个结构本身因为Human抽象类未初始化的原因,say方法是无效的.
当然如果父类已经初始化了,用ST这样做也是可以的!

现在来看一下完整例子的正确的运行结果.

Kid's gender is Male

Kid's age is 3

reference count is 2

reference count is 1

这里要说的就是我们开始在堆上分配了两个Kid实例,所以第一个计数值是2,
后代删除了一个,所以计数值打印出来就是1了.
但是在把两个Kid删了之后,如果我们注释掉上一段最后的那一句,打印出来的却不是0!
这是为何呢?!

原因很简单,因为此时静态部分已经被删了啊!
如果你还不放心,想确定一下什么时候那个技术值变为零了,那么改一下Kid类的析构函数,具体如下.
\starttyping[option=c]
//DTOR(Kid){ DTOR_ST(Kid)}
DTOR(Kid){ 
	DTOR_ST(Kid) 
	printf("after dtor of the StKid the reference count is %d\n",
			THIS->Kid_reference); 
}
\stoptyping
改了之后正确的输出结果如下,
Kid's gender is Male

Kid's age is 3

reference count is 2

reference count is 1

after dtor of the StKid the reference count is 0

可以看到,在静态结构体析构时,那个计数器已经变为0了.

\section{休息一下}
每章的最后一部分都不会有什么新的东西,这一节也是如此.

要说的是这一节又引入了4个宏CLASS_ST,CTOR_ST,DTOR_ST以及ST宏!
但从记忆的角度,并没有带来太多负担,
因为其设计基本就是原来的宏加上ST这样的形式,并且只需按需使用.
对于一些简单的小程序完全可以不用.
再次为了强化记忆,依旧以一个名为A的类为例写一段最最简单的代码.
\starttyping[option=c]
CLASS(A){ CLASS_ST(A) int a; };
CTOR(A){ CTOR_ST(A) THIS->a=100 }
DTOR(A){ DTOR_ST(A) }
int main(){
	A *a=newA();
	StA *f=ST(A);
	printf("the a in the static part is %d",f->a);
	delA(&a);
	return 0;
}
\stoptyping

值得注意的是,虽然多了静态部分,
但是main函数调用中却无需管理那一部分的内存,这得益于简易的引用计数机制.

仅仅是这样就够了吗?欲望总是不能被简单满足的,编程的欲望尤为如此!
所以某人曾总结过\quotation{生命不息,折腾不止}
正如上一章结尾所述,再加上本节遇到的诸如ST宏使用上的\quotation{不爽},
我们已经发现了很多目前的宏的缺陷,比如
\startitemize[n]
\item ST宏使用可能带来的异常
\item 返回值的管理
\item 错误检测机制
\item 继承关系中父类构造与析构函数的自动调用
\item 堆内存的检测
\item 构造函数中静态部分的调用顺序问题
\item ......
\stopitemize

所以仍有很多问题需要解决,但是真正解决了这些问题我们肯定会引入大量额外的代码以及不少额外的宏,
即使我们引入的代码足够优秀,宏也足够明晰,但对于某些设备来说,反倒不适用了(比如100MHZ一下的嵌入式设备).
所以把前面的宏总结了一下,随文档一起或以OOC-LITE的名义单独发布,读者可以按需使用.
 
另外要说的是上面这些问题以及一些我还没来得及说的问题有相当一部分已经在\from[ooc]完整版本中解决,
你可以直接参阅其源码,也可以待我后文的慢慢介绍,要注意的是在完整版本中STATIC宏对应简化版的CLASS_ST
宏,这样做是有原因的,因为完整版的宏设计的更加强大,这样的改动会使其更加美观.
而在简化版中使用CLASS_ST则与CTOR_ST和DTOR_ST对应,这样更易于记忆
(完整版无需CTOR_ST和DTOR_ST宏的显示调用,因为有更强大的ASM宏)

\chapter{体侧运动}
扭一下身子,换个角度,看看还有哪些不足.

初稿中并没有这一章节,加上这一节的主要目的是引入ASM宏,
一来使得代码更为简洁,二来使代码风格与完整版保持一致,而从ASM宏本身的实现来说,
这一节中的版本是一个比较简单的版本,相较完整版的一个缺点是不能实现构造析构函数的动态改变.
另外,在这一节中因为ASM宏的使用,我们不用再去用CTOR和DTOR宏了,所以有一些风格上的改变,
将原来的CLASS_ST重定义为STATIC,另外CLASS宏也增加一个别名CLASS_EZ,注意简化版的CLASS_EZ和CLASS
完全相同,但是完整版本中的是不同的!
\section{将CTOR&DTOR合二为一的ASM_EZ宏}
为了方便,还是从不带静态部分的说起,现在假设我们还没有静态部分.

原先的CTOR宏和DTOR宏虽然是全开放的,看起来本身相比半包形式的宏舒服不少.
但毕竟不是原生的函数形式.其实要做到这一点并不难,一个简单的做法就是,
我们只需要再加一层,在原来的ini函数和fin函数中再额外调用独立的函数完成
构造和析构的函数.

\stoptext
